package node;

/**
 *
 * @author Vojta
 */
public class AVLNode extends BinaryNode {

    protected int balance;

    public AVLNode(int id) {
        super(id);
    }

    /**
     * Metoda prida uzlu potomka predaneho v parametru.
     * Zavola metodu predka BinaryNode addChild() a pote provede vyvazeni stromu.
     * @param n Pridavany uzel
     * @return True, pokud pridani uspeje, false, pokud neuspeje.
     */
    @Override
    public boolean addChild(BinaryNode n) {
        boolean isInserted = super.addChild(n);
        countBalance();
        if (isInserted && Math.abs(balance) > 1) {
            if (balance > 0) {
                if (leftChild.compareTo(n) == 1) {
                    rotateRight();
                } else {
                    rotateLeftRight();
                }
            } else {
                if (rightChild.compareTo(n) == -1) {
                    rotateLeft();
                } else {
                    rotateRightLeft();
                }
            }
        }
        return isInserted;
    }

    /**
     * Metoda aktualizuje hodnotu rovnovahy uzlu.
     */
    private void countBalance() {
        int left = (leftChild != null) ? leftChild.subtreeHeight() : 0;
        int right = (rightChild != null) ? rightChild.subtreeHeight() : 0;
        balance = left - right;
    }

    /**
     * Metoda provede rotaci uzlu vlevo.
     */
    private void rotateLeft() {
        AVLNode temp = (AVLNode) rightChild;        //Potrebujeme si uchovat referenci na praveho potomka (budouci koren substromu)
        rightChild = rightChild.getLeftChild();     //Jako praveho potomka nastavime leveho potomka budouciho korene
        if (rightChild != null) {                   //Pokud novy pravy potomek existuje, nastavime mu noveho rodice
            rightChild.setParent(this);
        }
        setParentOfSubtreeAfterRotation(temp);
        parent = temp;                              //Nastavime rodice rotovaneho uzlu na budouci koren substromu
        parent.setLeftChild(this);                  //Budoucimu koreni substromu nastavime jako leveho potomka rotovany uzel
    }

    /**
     * Metoda provede rotaci uzlu vpravo.
     */
    private void rotateRight() {
        AVLNode temp = (AVLNode) leftChild;
        leftChild = leftChild.getRightChild();
        if (leftChild != null) {
            leftChild.setParent(this);
        }
        setParentOfSubtreeAfterRotation(temp);
        parent = temp;
        parent.setRightChild(this);
    }
    
    /**
     * Metoda provede pravo-levou rotaci.
     * Nejprve rotuje praveho potomka uzlu vpravo a pote uzel vlevo.
     */
    private void rotateRightLeft() {
        ((AVLNode) rightChild).rotateRight();
        rotateLeft();
    }
    
    /**
     * Metoda provede levo-pravou rotaci.
     * Nejprve rotuje leveho potomka uzlu vlevo a pote uzel vpravo.
     */
    private void rotateLeftRight() {
        ((AVLNode) leftChild).rotateLeft();
        rotateRight();
    }
    
    /**
     * Metoda resi spravne umisteni substromu pri rotaci.
     * Zjisti pozici puvodniho korene substromu vuci jeho rodici a pote nastavi spravne reference
     * budoucimu koreni substromu.
     * @param n Budouci koren substromu.
     */
    private void setParentOfSubtreeAfterRotation(AVLNode n) {
        switch (getNodePosition()) {
            case ROOT:
                n.setParent(null);
                break;
            case LEFT_SUBTREE:
                n.setParent(parent);
                parent.setLeftChild(n);
                break;
            case RIGHT_SUBTREE:
                n.setParent(parent);
                parent.setRightChild(n);
                break;
        }
    }
}
